/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/10 12:38
  *
  * @file       hd_oled_protocol_spi.c
  * @brief      OLED SPI Communication Protocol Hardware Driver.
  *
  * @note       This file contains the SPI Communication Protocol hardware
  *             driver for the OLED.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2024-06-10     Chris        the first version
  *
  ******************************************************************************
  */

#include <delay.h>
#include <air001xx_ll_gpio.h>
#include "hd_oled_protocol_spi.h"


static void OLEDProtocolSPI_init(OLEDProtocol *super) {
    OLEDProtocolSPI *this = (OLEDProtocolSPI *) super;

    this->dc->init(this->dc);   // data or command
    this->cs->init(this->cs);   // Chip Select(slave select)

    // OLED need a power on reset
    if (this->res) {
        this->res->init(this->res);
        this->res->GPIOx->BSRR = this->res->Pin << 16;
        delay_ms(100);
        this->res->GPIOx->BSRR = this->res->Pin;
    }

    this->spi->stop(this->spi, this->cs);
}

static void OLEDProtocolSPI_write(OLEDProtocol *super,
                                  uint8_t nc, uint16_t datOrCmd, uint8_t *dat, uint8_t len) {
    OLEDProtocolSPI *this = (OLEDProtocolSPI *) super;

    // Set data or command
    this->dc->GPIOx->BSRR = (datOrCmd == 0 ? (this->dc->Pin << 16) : this->dc->Pin);

    this->spi->start(this->spi, this->cs);

    while (len--) {
        this->spi->swapByte(this->spi, *dat++);
    }

    this->spi->stop(this->spi, this->cs);
}

static OLEDProtocolSPI *build(SPI *spi, GPIO *res, GPIO *dc, GPIO *cs) {
    OLEDProtocolSPI *oledProtocolSpi = malloc(sizeof(OLEDProtocolSPI));

    Driver_add(oledProtocolSpi, GEN_PERM);

    res->Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    dc->Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
    cs->Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

    oledProtocolSpi->spi = spi;
    oledProtocolSpi->res = res;
    oledProtocolSpi->dc  = dc;
    oledProtocolSpi->cs  = cs;

    OLEDProtocol *oledProtocol = OLEDProtocolBuilder.build();
    oledProtocolSpi->oledProtocol       = *oledProtocol;
    oledProtocolSpi->oledProtocol.init  = &OLEDProtocolSPI_init;
    oledProtocolSpi->oledProtocol.write = &OLEDProtocolSPI_write;

    return oledProtocolSpi;
}

const struct OLEDProtocolSPIClass OLEDProtocolSPIBuilder = {.build = &build};

